﻿// ==C# class for .NET Framework 2.0==
// @name Bazzinga!
// @language C#
// @namespace NBazzinga
// @description Уникализация текстов через Bazzinga.org
// @author sh.rustam@gmail.com

using System;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace NBazzinga
{
    /// <summary>
    /// Класс для работы с синонимайзером bazzinga.org
    /// </summary>
    public class Bazzinga
    {
        private const string mainhref = "http://bazzinga.org/"; // Главный урл Баззинги
        private const int max_chars = 10000; // Сколько максимум символов в одном синонимайзинге
        private const int min_words = 5; //  Сколько слов минимум требует синонимайзинг
        private const string useragent = "Bazzingoid 1/2"; // Агент Баззинги
        private bool logged_in;

        public Bazzinga(Settings settings)
        {
            if (settings == null)
            {
                throw new ArgumentNullException(
                    "settings", "Конструктор класса при создании требует настройки, а не всякое там.");
            }

            _settings = settings;
        }

        private Settings _settings { get; set; }

        /// <summary>
        /// Синонимизировать текст
        /// </summary>
        /// <param name="text">текст для синонимизации</param>
        /// <returns>синонимизированный текст</returns>
        public string UniqueText(string text)
        {
            if (!logged_in)
            {
                LogIn();
            }
            if (string.IsNullOrEmpty(text))
            {
                throw new ArgumentException("Нет текста");
            }

            if (text.Length > max_chars)
            {
                throw new ArgumentException(string.Format("Превышено допустимое количество символов ('{0}') - {1}",
                                                          max_chars, text.Length));
            }

            int wordscount = Regex.Split(text, @"\s+").Length;
            if (wordscount < min_words)
            {
                throw new ArgumentException(string.Format("Не хватает слов - надо не меньше {0}", min_words));
            }

            string pim5 = text; //Regex.Replace(text, @"\s|\t|\r|\n", "");
            while (pim5.Contains(" ") || pim5.Contains("\t") ||
                   pim5.Contains("\r") || pim5.Contains("\n"))
            {
                pim5 = pim5.Replace(" ", "");
                pim5 = pim5.Replace("\t", "");
                pim5 = pim5.Replace("\r", "");
                pim5 = pim5.Replace("\n", "");
            }

            byte[] utf8 = Encoding.UTF8.GetBytes(pim5);
            string base64 = Convert.ToBase64String(utf8);
            string md5_check = Md5(base64);
            string input = Uri.EscapeDataString(text);
            //var input = Microsoft.JScript.GlobalObject.encodeURIComponent(text);

            byte[] post =
                Encoding.UTF8.GetBytes(string.Format("input={0}&highlight={1}&inputmd5={2}&seo_opt=true", input,
                                                     _settings.Highlight, md5_check));
            string result = GetURL(string.Format("{0}{1}", mainhref, "magic.html"), post, _settings.Cookie);

            if (result.Contains("http://bazzinga.org/api.html"))
            {
                logged_in = false;
                throw new Exception("Вероятно, ошибка авторизации");
            }
            else if (result == "Кажется тут где-то какая-то ошибка. Проверьте правильно ли вы указали логин и пароль." ||
                     result.Contains(
                         "Извините, но мы получили не весь текст. На всякий случай мы не будем его обрабатывать.") ||
                     result.Contains("Этот текст обработать не получится - в нем превышен лимит в 10000 символов."))
            {
                throw new Exception(result);
            }
            else
            {
                return result;
            }
        }

        private void LogIn()
        {
            if (string.IsNullOrEmpty(_settings.Login))
            {
                throw new Exception("Нужен логин!");
            }
            if (string.IsNullOrEmpty(_settings.Password))
            {
                throw new Exception("Нужен пароль!");
            }

            string key = GetKey();
            string parameters = string.Format("login={0}&pass={1}", _settings.Login,
                                              Md5(string.Concat(Md5(_settings.Password), key)));
            string result = GetURL(
                string.Format("{0}{1}", mainhref, "inc/auth.php"),
                Encoding.UTF8.GetBytes(parameters),
                _settings.Cookie);

            if (result.Length > 0)
            {
                throw new Exception(result);
            }
            else
            {
                logged_in = true;
            }
        }

        private string GetKey()
        {
            WebResponse result = GetURL(string.Format("{0}{1}", mainhref, "inc/keys.php"));
            string response;
            using (var sr = new StreamReader(result.GetResponseStream()))
            {
                response = sr.ReadToEnd().Trim();
            }

            if (string.IsNullOrEmpty(response))
            {
                throw new Exception("Не удалось получить ключ.");
            }

            string cookie = result.Headers["Set-Cookie"];
            if (string.IsNullOrEmpty(cookie))
            {
                throw new Exception("Не удалось получить cookie для ключа");
            }
            _settings.Cookie = cookie;

            string key = Regex.Replace(response, "<(.|\n)*?>", "");
            if (string.IsNullOrEmpty(key))
            {
                throw new Exception("Не удалось получить ключ из контента");
            }
            else
            {
                return key;
            }
        }

        private string GetURL(string href, byte[] post, string cookie = null)
        {
            return new Http(_settings.WebProxy, useragent).HttpPost(href, post, cookie);
        }

        private WebResponse GetURL(string href)
        {
            return new Http(_settings.WebProxy, useragent).HttpPost(href);
        }

        private string Md5(string input)
        {
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.ASCII.GetBytes(input);
            byte[] hash = md5.ComputeHash(inputBytes);

            var sb = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                sb.Append(hash[i].ToString("X2"));
            }
            return sb.ToString().ToLower();
        }
    }
}